package support;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import cinvestment.Plant;
import util.Lang;
import util.MathUtil;

public class AlgorithmSupport {

	public static final String PAIRING = "pairing";
	public static final String CHAINING = "chaining";
	public static final String W_SHAPE = "wshape";
	public static final String TAILORED_PAIRING = "tailored_pairing";
	public static final String TAILORED_CHAINING = "tailored_chaining";
	public static final String TAILORED_W_SHAPE = "tailored_wshape";
	public static final String EXTENSIVE = "extensive";
	public static final String UNBALANCE_CHAIN = "unbalance_chain";
	public static final String OPEN_CHAIN = "open_chain";
	public static final String TOTAL_FLEXIBILITY = "total_flexibility";

	public static final long TERMINATE_TIME_LIMITE = 100000;// ms

	/**
	 * @param sn
	 *            Instance count
	 * @param n
	 *            Length for a single instant
	 * **/
	public static double[][] getDemands_uniform(int sn, int n, double min, double max) {
		double[][] data = null;
		ExcelOperator eo = new ExcelOperator();
		String path = getUniformDemandFilePath(sn, n);
		data = eo.read(path, "demand");
		if (data == null || data[0] == null) {
			System.out.println("create data");
			data = MathUtil.uniform(sn, n, min, max);
			eo.write(path, DEMAND_SHEET_NAME, data, null);
		}
		return data;
	}

	/**
	 * @param sn
	 *            Instant count
	 * @param n
	 *            Length for a single instant
	 * @param variance
	 *            variance
	 * **/
	public static double[][] getDemands_normal(int sn, int n, double mean, double variance, double low, double upper) {
		double[][] data = null;
		ExcelOperator eo = new ExcelOperator();
		// String path = getNormDemandFilePath(sn, n);
		String path = getNormDemandFilePath(sn, n, variance);
		data = eo.read(path, "demand");
		if (data == null || data[0] == null) {
			// System.out.println("create data");
			double std = Math.pow(variance, 0.5);
			data = MathUtil.normal(sn, n, mean, std, low, upper);
			eo.write(path, DEMAND_SHEET_NAME, data, null);
		}
		return data;
	}

	/**
	 * @param path
	 *            complete path for the result file
	 * @param sheetName
	 *            name of sheet to store the result. If sheetName is null, use
	 *            default sheet name of excel
	 * @param list
	 *            main simulation data to be stored
	 * @param simulationInfo
	 *            parameter settings of the simulation
	 * @param capHead
	 *            head line for capacities sheet
	 * **/
	public static void storeFinalResult(String path, String sheetName, List<List<Double[]>> list, double[] delta,
			List<String> capHead, List<String> simulationInfo, int productTypes) {
		double[][] obj = new double[list.size()][1];
		double[][] cap = new double[list.size()][delta.length];

		for (int i = 0; i < list.size(); i++) {
			obj[i] = Lang.clone(Lang.convertDouble2double(list.get(i).get(0)));
			cap[i] = Lang.clone(Lang.convertDouble2double(list.get(i).get(1)));
		}

		if (path == null)
			path = Lang.projectRoot() + "\\data\\result\\finalResult_" + productTypes + "_"
					+ dateFormat.format(new Date()) + ".xlsx";
		ExcelOperator eo = new ExcelOperator();

		eo.writeInfo(path, sheetName, simulationInfo);

		List<String> headForObj = new ArrayList<String>();
		headForObj.add("obj");
		eo.write(path, sheetName, obj, headForObj, simulationInfo.size() + 2, 0);
		eo.write(path, sheetName, cap, capHead, simulationInfo.size() + 2, 2);

	}

	public static void storeResult(List<Double[]> data, int sn, int n) {
		ExcelOperator eo = new ExcelOperator();
		String path = getResultFilePath(sn, n);
		eo.write(path, RESULT_SHEET_NAME, data);
	}

	public static String getUniformDemandFilePath(int sn, int n) {
		String path = Lang.projectRoot() + "\\data\\demand\\damand_" + sn + "_" + n + ".xlsx";
		return path;
	}

	public static String getNormDemandFilePath(int sn, int n) {
		String path = Lang.projectRoot() + "\\data\\demand\\normal_" + sn + "_" + n + ".xlsx";
		return path;
	}

	public static String getNormDemandFilePath(int sn, int n, double var) {
		String path = Lang.projectRoot() + "\\data\\demand\\normal_" + sn + "_" + n + "_" + var + ".xlsx";
		return path;
	}

	private static String getResultFilePath(int sn, int n) {
		String path = Lang.projectRoot() + "\\data\\result\\result_" + sn + "_" + n + "_"
				+ dateFormat.format(new Date()) + ".xlsx";
		return path;
	}

	private static String DEMAND_SHEET_NAME = "demand";

	private static String RESULT_SHEET_NAME = "result";

	private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMdHHmm");

	/**
	 * Create a list of plants which are potential to produce all types of
	 * combinations of products, i.e., products indexes are {0,1,2} then list of
	 * plants are power subset of {0,1,2}
	 * 
	 * @param N
	 *            number of products that the plant list can produce
	 * **/
	public static List<Plant> createExtensivePlants(int N) {
		List<Plant> plants = new ArrayList<>();
		int[] set = MathUtil.getSetFromZ(N);
		List<List<Integer>> subSetList = MathUtil.subSetOrder(set);
		for (List<Integer> subList : subSetList) {
			Plant plant = new Plant();
			plant.setAvailableProducts(subList);
			plants.add(plant);
		}
		return plants;
	}

	/**
	 * create a balanced chaining plants set, i.e., if N = 3, return
	 * {[0,1],[1,2],[2,0]}
	 * **/
	public static List<Plant> createChainingPlants(int N) {
		List<Plant> plants = new ArrayList<>();
		for (int i = 0; i < N; i++) {
			Plant plant = new Plant();
			List<Integer> availableSet = new ArrayList<>();
			availableSet.add(i);
			if (i == N - 1)
				availableSet.add(0);
			else
				availableSet.add(i + 1);
			plant.setAvailableProducts(availableSet);
			plants.add(plant);
		}
		return plants;
	}

	public static List<Plant> createTailoredChainingPlants(int N) {
		List<Plant> plants = createChainingPlants(N);
		addingTailored(plants, N);
		return plants;
	}

	/**
	 * create a balanced pairing plants set, i.e., if N = 3, return
	 * {[0,1],[1,2]}
	 * **/
	public static List<Plant> createWShapePlants(int N) {
		List<Plant> plants = new ArrayList<>();
		for (int i = 0; i < N - 1; i++) {
			Plant plant = new Plant();
			List<Integer> availableSet = new ArrayList<>();
			availableSet.add(i);
			availableSet.add(i + 1);
			plant.setAvailableProducts(availableSet);
			plants.add(plant);
		}
		return plants;
	}

	/**
	 * number of plants n is one less than that of products N. create a
	 * structure from Deng's design
	 * **/
	public static List<Plant> createUnbalanceChain(int N) {
		List<Plant> chaining = createChainingPlants(N - 1);
		chaining.get(0).getAvailableProducts().add(N - 1);
		int farest = N / 2;
		chaining.get(farest).getAvailableProducts().add(N - 1);
		return chaining;
	}

	public static List<Plant> createOpenChain(int N) {
		List<Plant> list = new ArrayList<>();
		for (int i = 0; i < N - 1; i++) {
			Plant plant = new Plant();
			List<Integer> availableSet = new ArrayList<>();
			availableSet.add(i);
			availableSet.add(i + 1);
			plant.setAvailableProducts(availableSet);
			list.add(plant);
		}
		Plant plant = new Plant();
		List<Integer> availableSet = new ArrayList<>();
		availableSet.add(N - 1);
		plant.setAvailableProducts(availableSet);
		list.add(plant);
		return list;
	}

	private static void addingTailored(List<Plant> plants, int N) {
		for (int i = 0; i < N; i++) {
			Plant dedicated = new Plant();
			List<Integer> avail = new ArrayList<>();
			avail.add(i);
			dedicated.setAvailableProducts(avail);
			plants.add(dedicated);
		}
	}

	public static List<Plant> createTailoredWShapePlants(int N) {
		List<Plant> plants = createWShapePlants(N);
		addingTailored(plants, N);
		return plants;
	}

	public static List<Plant> createPairingPlants(int N) {
		List<Plant> plants = new ArrayList<>();

		int[] set = MathUtil.getSetFromZ(N);
		for (int i = 0; i < N - 1; i++) {
			for (int j = i + 1; j < N; j++) {
				Plant plant = new Plant();
				List<Integer> availableSet = new ArrayList<>();
				availableSet.add(set[i]);
				availableSet.add(set[j]);
				plant.setAvailableProducts(availableSet);
				plants.add(plant);
			}
		}
		return plants;
	}

	public static List<Plant> createTailoredPairing(int N) {
		List<Plant> plants = createPairingPlants(N);
		addingTailored(plants, N);
		return plants;
	}

	/** balanced totally flexibility **/
	public static List<Plant> createTotalFlexibility(int N) {
		List<Plant> plants = new ArrayList<>();
		for (int i = 0; i < N; i++) {
			Plant plant = new Plant();
			List<Integer> availableSet = new ArrayList<>();
			for (int j = 0; j < N; j++)
				availableSet.add(j);
			plant.setAvailableProducts(availableSet);

			plants.add(plant);
		}

		return plants;
	}

	public static List<Plant> createPlantsByStructure(int N, String structure) {
		List<Plant> plants = new ArrayList<>();
		switch (structure) {
		case AlgorithmSupport.W_SHAPE:
			plants = createWShapePlants(N);
			break;
		case AlgorithmSupport.CHAINING:
			plants = createChainingPlants(N);
			break;
		case AlgorithmSupport.EXTENSIVE:
			plants = createExtensivePlants(N);
			break;
		case AlgorithmSupport.PAIRING:
			plants = createPairingPlants(N);
			break;
		case AlgorithmSupport.TAILORED_CHAINING:
			plants = createTailoredChainingPlants(N);
			break;
		case AlgorithmSupport.TAILORED_PAIRING:
			plants = createTailoredPairing(N);
			break;
		case AlgorithmSupport.TAILORED_W_SHAPE:
			plants = createTailoredWShapePlants(N);
			break;
		case AlgorithmSupport.UNBALANCE_CHAIN:
			plants = createUnbalanceChain(N);
			break;
		case AlgorithmSupport.OPEN_CHAIN:
			plants = createOpenChain(N);
			break;
		case AlgorithmSupport.TOTAL_FLEXIBILITY:
			plants = createTotalFlexibility(N);
			break;
		default:
			break;
		}
		return plants;
	}
}
